# vim: tabstop=4 : expandtab : shiftwidth=4

from thing import *
from mob import *
import color
import races
from channel import *
import quests
import devices

def roomuid(actor, target):
    room = fetch(actor.location)
    actor.hear("Room UID: "+str(room.uid), color.magenta)

def build(actor, target):
    room = fetch(actor.location)
    try:
        directionTo, directionFrom = target.split()
    except:
        actor.hear("Malformed arguments!", color.red)
        return
    newroom = Room()
    room.exits[directionTo] = newroom.uid
    newroom.exits[directionFrom] = room.uid
    if actor.biome_set: newroom.biome = actor.biome_set
    actor.hear("New room built (UID "+str(newroom.uid)+").", color.magenta)
    if actor.biome_set == None:
        actor.hear("WARNING! You do not have a biome set for room building. 'None' applied.", color.red)
    else: actor.hear("Biome = "+actor.biome_set, color.magenta)

def setBiome(actor, target):
    room = fetch(actor.location)
    biome_options = ['forest', 'city', 'cave', 'swamp', 'plains',
                     'mountain', 'tundra']
    if target == 'list':
        for biome in biome_options:
            actor.hear(biome)
    if target in biome_options:
        room.biome = target
        actor.hear(room.name+" is now set as a "+target+" biome", color.magenta)
    else:
        actor.hear("That is not a valid option", color.red)
    if target == "build":
        actor.hear("Please set you building biome now...")
        actor.biome_set = actor.input()
        if actor.biome_set == None: return
        if actor.biome_set in biome_options:
            actor.hear("Your building biome is now "+actor.biome_set+".", color.magenta)
        else:
            actor.hear("That is not a valid option!", color.red)

def roomname(actor, target):
    room = fetch(actor.location)
    room.name = target
    actor.hear("The room's name is now "+target)

def roomdesc(actor, target):
    room = fetch(actor.location)
    room.desc = target

def setexit(actor, target):
    room = fetch(actor.location)
    if "room" not in room.attrs:
        actor.hear("You are not inside a room.", color.red)
        return
    direction,sep,targetUID = target.partition(" ")
    if targetUID.isdigit():
        targetRoom = fetch(int(targetUID))
        if targetRoom != None:
            if "room" not in targetRoom.attrs: actor.hear("Warning, the exit you are creating does not lead to a room.", color.yellow)
        if direction in DIRECTIONS.keys():    
            room.exits[DIRECTIONS[direction]] = int(targetUID)
            actor.hear("You tear a hole in time and space to create a new exit from this room.", color.magenta)
            room.broadcast(actor.name+" tears a hole in time and space, creating a new exit.", color.magenta, actor)
        else:
            actor.hear("That is not a standard exit. Exit not set!", color.red)
    else:
        actor.hear("That is not a valid UID.", color.red)

def renameExit(actor, cmd):
    """Syntax:
    rename <old direcion> <new direction>
NOTE: This keeps the same UID value. Does not change where the exit leads"""
    target,sep,new = cmd.partition(' ')
    room = fetch(actor.location)
    if "room" not in room.attrs:
        actor.hear("You are not inside a room.", color.red)
        return
    if target in room.exits.keys():
        room.exits[new] = room.exits[target]
        room.exits.pop(target)
        actor.hear("You have successfully renamed %s to %s."%(target, new))
    else: actor.hear("That is not a valid exit.", color.red)
        

def deleteExit(actor, target):
    room = fetch(actor.location)
    if "room" not in room.attrs:
        actor.hear("You are not inside a room.", color.red)
        return
    if target in room.exits.keys():
        if len(room.exits.keys()) == 1:
            actor.hear("You will be trapped here if you delete this room. Proceed? <return to abort, 'yes' to delete>", color.red)
            yesorno = actor.input()
            if yesorno == None or yesorno == "":return
        room.exits.pop(target)
        actor.hear("You delete the exit to the "+target+".", color.magenta)
    else: actor.hear("That is not a valid exit.", color.red)

def takeable(actor, target):
    room = fetch(actor.location)
    thing = Takeable()
    thing.moveInto(room.uid)
    if target:
        thing.name = target
    actor.hear("You summon an object from the ambient magic in the air.", color.magenta)
    room.broadcast(actor.name+" summons an object from the ambient magic in the air!", color.magenta, actor)

def forge(player, target):
    room = fetch(player.location)
    item = Equipable()
    item.bonus={}
    if target:
        item.name = target
    else:
        player.hear("Name your weapon:")
        item.name = player.input()
        if item.name == None:
            item.end()
            return

    #Prompt to set the items attributes
    player.hear("What location will this item be equippable to?")
    player.hear("Options: mainhand, offhand, head, face, ears, fingers, neck, torso, waist, legs, feet")
    slot = player.input()
    if slot == None:
        item.end()
        return
    item.slot = slot
    if item.slot == 'mainhand' or item.slot == 'offhand':
        possible_types = ['edged', 'blunt', 'axe', 'ranged', 'piercing', 'throwing']
        player.hear("You've chosen a wieldable item. Please set the architecture for this item")
        while True:
            for itype in possible_types:
                player.hear("   "+itype)
            itype = player.input()
            if itype == None:
                return
            elif itype not in possible_types:
                player.hear("That's not an option. Try again.")
            else:
                break
        item.arch = itype
        player.hear("Item architecture set to "+itype+".")
        player.hear("")
    player.hear("What attributes will be granted to the hero equipping this item?")
    possible_attrs = ['str', 'dex','int','con','maxHP','maxMP','dodge','accuracy','threat']
    while True:
        player.hear("Please choose from one of the following options, or type 'done' when finished.")
        for value in possible_attrs:
            player.hear("    "+value)

        attr = player.input()
        if attr == None:
            item.end()
            return
        if attr == "done":
            player.hear("Your "+item.name+" has been forged!")
            break
        if attr not in possible_attrs:
            player.hear("That's not an option, please try again")
            continue
        player.hear("What value would you like to assign to "+attr+"?")
        bonus = player.input()
        if bonus == None:
            item.end()
            return
        if bonus.isdigit() == True:  item.bonus[attr] = int(bonus)
        else:
            player.hear("You'll want to use a number here")
    player.hear("Name: "+item.name)
    player.hear("slot: "+str(item.slot))
    for bonus in item.bonus.keys():
        player.hear(bonus+": "+str(item.bonus[bonus]))
    player.hear("Is this okay?")
    yesno = player.input()
    if yesno == None:
        item.end()
        return
    if yesno not in ["yes", "y"]:
        item.end()
        player.hear("Reforging...", color.red)
        forge(player, target)
    item.moveInto(room.uid)
    player.hear("You summon an object from the ambient magic in the air.", color.magenta)
    room.broadcast(player.name+" summons an object from the ambient magic in the air!", color.magenta, player)

def mailbox(actor, target):
    room = fetch(actor.location)
    box = Mailbox()
    box.name = "mailbox"
    box.desc = "A shiny brass mailbox."
    box.moveInto(room.uid)

def destroy(actor, target):
    room = fetch(actor.location)
    thing = findItem(actor, target)
    if thing:
        thing.hear(actor.name+" shatters you from existence.", color.red)
        thing.end()
        actor.hear("You shatter "+thing.name+" from existence.", color.magenta)
        room.broadcast(actor.name+" shatters "+thing.name+" from existence.", color.magenta, actor)

def purge(actor, blank):
    """ Type 'purge' to remove everything in the game that isn't a room, a player, or an equipable item.
    This should prevent the need to delete mudworld.yml if a tick crashes. Can just purge, and restart server.
    This is a powerful command... it will delete a lot of stuff... be careful."""
    hitList = []
    for thing in Thing.List:
        thing = fetch(thing)
        if thing != None:
            if 'player' not in thing.attrs:
                if 'room' not in thing.attrs:
                    if 'equipable' not in thing.attrs:
                        if "spawner" not in thing.attrs:
                            hitList.append(thing)
    for i in hitList:
        try: i.end()
        except: pass
        del i
    actor.hear("The world has been purged.", color.green)

def delSpawner(actor, blank):
    """Deletes all spawners in the current room."""
    hitList = {}
    room = fetch(actor.location)
    for uid in room.contents:
        spawner = fetch(uid)
        if spawner == None: continue
        if 'spawner' in spawner.attrs:
            hitList[spawner] = spawner.mobname
    actor.hear("The following mobs will no longer spawn here:", color.green)
    for mob in hitList.values():
        actor.hear(mob, color.red)
    for spawner in hitList.keys():
        spawner.end()
        del spawner

def prefab(actor, target):
    room = fetch(actor.location)
    try:
        f = open("prefabs.yml", "r")
    except IOError:
        actor.hear("No prefab file found!", color.red)
        return
    with f:
        prefabs = yaml.load(f)
    if target not in prefabs:
        actor.hear("No such prefab found.", color.red)
        return
    thing = Equipable()
    thing.name = prefabs[target].name
    thing.desc = prefabs[target].desc
    thing.bonus = prefabs[target].bonus
    thing.slot = prefabs[target].slot
    thing.arch = prefabs[target].arch

    thing.moveInto(room.uid)
    actor.hear("You summon a "+target+" into the room!", color.magenta)
    room.broadcast(actor.name+" summons a "+target+" into the room.", color.magenta, actor)

def gil(actor, quantity):
    location = fetch(actor.location)
    location.gil = int(quantity)
    actor.hear("You slap down %s gil, dwag!" % quantity, color.magenta)
    location.broadcast(actor.name+" slaps down %s gil." % quantity, color.magenta, actor)

def mob(actor, target):
    """
Syntax:
    mob [mobname]

If a mob name is not provided you will be asked for one.
"""
    room = fetch(actor.location)
    if not target:
        actor.hear("Name the mob:", color.blue)
        mobname = actor.input()
        if not mobname:
            return
    else: mobname = target
    actor.hear("Enter the mob's race:", color.blue)
    
    while 1:
        race = actor.input()
        if race == None:
            return
        if race not in races.mobRaces.keys():
            actor.hear("That's not a valid option at this time.")
            actor.hear("Valid options are: "+(", ".join(races.mobRaces.keys())))
            continue
        break
    while 1:
        actor.hear("Enter the mob's level:", color.blue)
        level = actor.input()
        if level == "": continue
        if level.isdigit() != True:
            actor.hear("Use a number for level")
            level = None
            continue
        else:
            level = int(level)
            break


    mob = Mob()
    mob.level = level
    mob.name = mobname
    mob.race = race
    races.raceMaker(mob)
    setAttributes(mob)
    mob.moveInto(room.uid)
    person_stat_updater(mob, True)
    calculator.levelup(mob, mob.stats['con'], mob.stats['int'], True)
    actor.hear("You summon "+mob.name+" from the magic in the air.", color.magenta)
    room.broadcast(actor.name+" summons "+mob.name+" from the magic in the air.",
color.magenta, actor)

def pacify(actor, target):
    """
Usage:
    pacify [mob]

If mob is given, that mob is angered. Otherwise this command pacifies all combat in the room.
"""
    room = fetch(actor.location)
    if not room:
        actor.hear("You cannot pacify in the Void.")
        return
    if target:
        item = findItem(actor, target)
        if not item: return
        if "mob" in item.attrs:
            item.attrs.discard("aggressive")
            item.inCombat = False
            actor.hear("You pacify "+item.name, color.magenta)
            room.broadcast(actor.name+" pacifies "+item.name, color.magenta, actor)
        else:
            actor.hear("You cannot pacify that.", color.red)
        return
    for uid in room.contents:
        thing = fetch(uid)
        if "person" in thing.attrs:
            thing.attrs.discard("aggressive")
            thing.inCombat = False
    actor.hear("With a wave of your handle, you quiet all combat.", color.magenta)
    room.broadcast("With a simple wave, "+actor.name+" quiets all combat.", color.magenta, actor)

def anger(actor, target):
    """
Usage:
    anger [mob]

If mob is given, that mob is angered. Otherwise, this command causes all mobs to attack.
"""
    room = fetch(actor.location)
    if not room:
        actor.hear("You cannot anger in the Void.")
    if target:
        item = findItem(actor, target)
        if not item: return
        if "mob" in item.attrs:
            item.attrs.add("aggressive")
            actor.hear("You anger "+item.name+"!", color.magenta)
            room.broadcast(actor.name+" angers "+item.name+"!", color.magenta, actor)
        else:
            actor.hear("You cannot anger that.", color.red)
        return
    for uid in room.contents:
        thing = fetch(uid)
        if "mob" in thing.attrs:
            thing.attrs.add("aggressive")
    actor.hear("You cause sudden hostility!", color.magenta)
    room.broadcast(actor.name+" causes sudden hostility!", color.magenta, actor)

def spawner(actor, blank):
    """
Usage:
    spawner

Sets an invisible spawner in the room that will spawn the spcified mob in the spcified
amount of time, but only to the specified quantity in that room."""
    room = fetch(actor.location)
    while 1:
        actor.hear("What is the name of the mob that will be spawning?")
        name = actor.input()
        if name == None:
            return 
        actor.hear("What will the race be of the mob spawning?")
        race = actor.input()
        if race == None:return 
        if race not in races.mobRaces.keys():
            actor.hear("That's not a valid thingumuhwutty!", color.red)
            continue
        actor.hear("Please now specify the range of levels separated by a space. ex '3 4'")
        lvls = actor.input()
        if lvls == None:
            return 
        minlvl,sep,maxlvl = lvls.partition(' ')
        if not minlvl.isdigit(): continue
        if not maxlvl.isdigit(): continue
        actor.hear("How many mobs will spawn?")
        quantity = actor.input()
        if quantity == None:
            return
        if not quantity.isdigit():continue
        actor.hear("How often will it spawn?")
        interval = actor.input()
        if interval == None:
            return 
        if not interval.isdigit():continue
        break
        
    spawner = Spawner()
    spawner.mobname = name
    spawner.mobs = race
    spawner.minlvl = int(minlvl)
    spawner.maxlvl = int(maxlvl)
    spawner.timer = int(interval)
    spawner.quantity = int(quantity)
    spawner.moveInto(room.uid)
    actor.hear("The spawnpoint has been set!, quick GTFO!", color.green)

def makeShop(actor, blank):
    room = fetch(actor.location)
    room.attrs.add('shop')
    actor.hear("You have zoned this room commercial.", color.green)

def stockShop(actor, item):
    room = fetch(actor.location)
    actor.hear("How much is this item going for?")
    price = actor.input()
    if price == None: return  
    try:
        f = open("prefabs.yml", "r")
    except IOError:
        actor.hear("No prefab file found!", color.red)
        return
    with f:
        prefabs = yaml.load(f)
    if item not in prefabs:
        actor.hear("No such prefab found.", color.red)
        return

    thing = Equipable()
    thing.name = prefabs[item].name
    thing.desc = prefabs[item].desc
    thing.bonus = prefabs[item].bonus
    thing.slot = prefabs[item].slot
    thing.arch = prefabs[item].arch
    if "shop" in room.attrs:
        room.goods.update({thing.name:{'price':int(price), 'quantity':1, 'uid':thing.uid}})
    actor.hear("You have stocked 1 "+item+"(s) which are selling for "+price+".", color.green)

def newchannel(actor, target):
    if target:
        if target in Channel.List.keys():
            actor.hear("That channel already exists.", color.red)
            return

        # Possible channel colors
        colorChoices = {
            "red": color.red,
            "blue": color.blue,
            "yellow": color.yellow,
            "cyan":color.cyan,
            "magenta": color.magenta,
            "gold": color.darkyellow,
        }

        actor.hear("Enter a color for this channel (enter nothing to abort)", color.yellow)
        actor.hear("Possible choices: " + (", ".join(colorChoices.keys())), color.yellow)
        actor.hear(">", color.yellow)
        channelColor = actor.input()
        if channelColor == None: return
        if channelColor == "":
            actor.hear("Abort.", color.red)
            return
        if channelColor.lower() not in colorChoices:
            actor.hear("That is not a valid choice.", color.red)
            return

        # Create the channel
        Channel(target, colorChoices[channelColor])

        actor.hear("Channel created.", color.green)
    else:
        actor.hear("You must specify a channel name.", color.red)

def teleport(actor, target):
    if target.isdigit() == False: 
        actor.hear("Use a number, dwag!", color.red)
        return
    for uid in Thing.List.keys():
        room = fetch(uid)
        if room == None:return
        if 'room' in room.attrs:
            if str(room.uid) == target:
                otherRoom = fetch(actor.location)
                actor.moveInto(int(target))
                actor.hear("You fly off to "+room.name+"!", color.magenta)
                otherRoom.broadcast(actor.name+" flies off into the distance!", color.magenta)
    else: actor.hear("Hmm... that apparently goes nowhere...", color.red)

def setDoor(actor, cmd):
    target,sep,skill = cmd.partition(' ')
    room = fetch(actor.location)
    if target in DIRECTIONS.keys(): target = DIRECTIONS[target]
    if target == "":
        actor.hear("You need to specify a direction, dwag!", color.red)
        return
    if not skill.isdigit():
        actor.hear("The skill level needs to be *an* positive integer, dwag!!!!!!!!", color.red)
        return
    if target in room.exits.keys():
        adjacentRoom = fetch(room.exits[target])
        if adjacentRoom == None:
            actor.hear("This exit leads to nowhere. Please fix this before trying to make a door go nowhere...", color.red)
            return
        for direction in adjacentRoom.exits.keys():
            if room.uid == adjacentRoom.exits[direction]:
                adjacentRoom.doors[direction] = int(skill)
        room.doors[target] = int(skill)
        actor.hear("A door has been set to the "+target+" at the lockpick skill of "+str(room.doors[target]), color.green)

def mobCount(actor, blank):
    x = 0
    for uid in Thing.List:
        mob = fetch(uid)
        if mob == None: continue
        if 'mob' in mob.attrs:
            x += 1
    actor.hear("There are currently "+str(x)+" mobs in the world.", color.magenta)

def roomCount(actor, blank):
    x = 0
    for uid in Thing.List:
        room = fetch(uid)
        if room == None: continue
        if 'room' in room.attrs:
            x += 1
    actor.hear("There are currently "+str(x)+" rooms in the world.", color.magenta)

def itemCount(actor, blank):
    x = 0
    for uid in Thing.List:
        item = fetch(uid)
        if item == None: continue
        if 'takeable' in item.attrs:
            x += 1
    actor.hear("There are currently "+str(x)+" items in the world.", color.magenta)

def makeNPC(actor, name):
    """Syntax: 'npc <its name>' """
    room = fetch(actor.location)
    npc = NPC()
    npc.name = name
    npc.moveInto(actor.location)
    room.broadcast(name+" has been born!", color.magenta)

def setNPCAttrs(actor, target):
    room = fetch(actor.location)
    npc = findItem(actor, target)
    if 'npc' in npc.attrs:
        actor.hear("Here is "+npc.name+"'s current attributes.", color.green)
        actor.hear("="*75, color.gray)
        actor.hear("Training Offered: "+str(npc.training))
        actor.hear("Quests: "+str(npc.quests))
        actor.hear("="*75, color.gray)
        actor.hear("Please select from the following list of things to edit, or <return> to exit.", color.green)
        actor.hear("1: Training Offered, 2: Quests", color.gray)
        choice = actor.input()
        if choice == None: return
        if choice == "": return
        if choice == "1":
            while True:
                actor.hear("Please type out a full skill name. It will automatically be handled. <return> when finished", color.gray)
                skill = actor.input()
                if skill == None: return
                if skill == "": return
                if  skill in quests.POSSIBLE_SKILLS and skill not in npc.training:
                    npc.training.append(skill)
                    actor.hear(skill+" has been added.", color.green)
                elif skill in npc.training:
                    npc.training.remove(skill)
                    actor.hear(skill+" has been removed.", color.green)
                else: actor.hear("Uhh... something didn't work.", color.red)
        elif choice == "2":
            while 1:
                actor.hear("Please type out a full quest name. It will automatically be handled. <return> when finished", color.gray)
                quest = actor.input()
                if quest == None: return
                if quest == "": return
                if  quest not in npc.quests:
                    npc.quests.append(quest)
                    actor.hear(quest+" has been added.", color.green)
                elif quest in npc.quests:
                    npc.quests.remove(quest)
                    actor.hear(quest+" has been removed.", color.green)
                else: actor.hear("Uhh... something didn't work.", color.red)
        else: actor.hear("Shummuh shummuh whoobee whatee?", color.red)

def installDevice(actor, cmd):
    """Syntax 'install <device> <room UID> <direction of exit> (last is optional)
    The device will be isntall in the room you are located in, and will control the 
    room specified."""
    room = fetch(actor.location)
    device,  sep,remainder = cmd.partition(' ')
    roomUID, sep, direction = remainder.partition(' ')
    if roomUID.isdigit() == False:
        actor.hear("Room UID given was not a number.", color.red)
        return
    controlledRoom = fetch(int(roomUID))
    if controlledRoom == None:
        actor.hear("That's not a room... Double check your UID.", color.red)
        return
    if direction != "":
        if direction not in controlledRoom.exits.keys():
            actor.hear("There's no exit in that direction from room number "+roomUID+".", color.red)
            return
    for item in devices.DEVICE_LIST:
        if device in item[0]:
            Device = item[1]()
            Device.moveInto(room.uid)
            controlledRoom.devices.append(Device.uid)
            Device.controls['room'] = controlledRoom.uid
            if direction != "": Device.controls['direction'] = direction
            actor.hear("The "+Device.name+" has been installed for room "+str(roomUID)+".", color.green)
            if direction != "": actor.hear("The device manipulates the exit to the %s."%direction, color.green)
            return
    actor.hear("No device was found of that name.", color.red)

def displayAll(actor, cmd):
    x = 0
    for uid,thing in Thing.List.items():
        name = '...'
        if thing != None: name = thing.name
        x += 1
        if x > 25:
            actor.hear("...")
            actor.hear("The list goes on. Type 'm', or 'done'.")
            answer = actor.input()
            if answer == None: return
            if answer == 'done': return
            elif answer == 'm':
                x = 0
        actor.hear(str(uid)+": "+name)

commands = [
    (["roomuid"], roomuid),
    (["build"], build),
    (['setbiome'], setBiome),
    (['rn','roomname'], roomname),
    (['rd','roomdesc'], roomdesc),
    (['sete','setexit'], setexit),
    (['delexit'], deleteExit),
    (['rename'], renameExit),
    (["takeable"], takeable),
    (["forge"], forge),
    (["mailbox"], mailbox),
    (["des", "destroy"], destroy),
    (["purge"], purge),
    (['delspawner'], delSpawner),
    (["prefab"], prefab),
    (["gil"], gil),
    (["mob"], mob),
    (["pacify", "quiet"], pacify),
    (["anger", "incite"], anger),
    (["spawner"], spawner),
    (['shop'], makeShop),
    (['stock'], stockShop),
    (['newchannel'], newchannel),
    (['tport', 'teleport', 'port', 'zoom'], teleport),
    (['door'], setDoor),
    (['mobcount'], mobCount),
    (['roomcount'], roomCount),
    (['itemcount'], itemCount),
    (['npc'], makeNPC),
    (['editnpc'], setNPCAttrs),
    (['install'], installDevice),
    (['dis', 'displayall'], displayAll)
]

DIRECTIONS = {'n':'north','s':'south','e':'east','w':'west',
            'nw':'northwest','ne':'northeast','sw':'southwest','se':'southeast'}
